package fr.jade.fraclite.factory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.objectweb.fractal.api.factory.InstantiationException;
import org.objectweb.fractal.api.type.ComponentType;
import org.objectweb.fractal.api.type.InterfaceType;
import org.objectweb.fractal.api.type.TypeFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import fr.jade.fraclite.FracLite;

public class MembraneType {

	Map<String, ComponentType> membraneTypes = new HashMap<String, ComponentType>();
	Map<String, String> membraneImpls = new HashMap<String, String>();

	Map<String, InterfaceType> controlItfs = new HashMap<String, InterfaceType>();
	
	TypeFactory tf = null;

	public MembraneType(TypeFactory tf) throws ChainedInstantiationException {
		this.tf = tf;
		String config = System.getProperty("fraclite.config");
		int b = 0;
    int i;
    do {
      i = config.indexOf(',', b);
      String file;
      if (i == -1) {
        file = config.substring(b);
      } else {
        file = config.substring(b, i);
        b = i + 1;
      }
      load(file);
    } while (i != -1);
	}

	public void load(String configFileName) throws ChainedInstantiationException {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder db = factory.newDocumentBuilder();

			InputStream configIS = null;
			if (configFileName != null) {
				// look for the user-specified FracLite configuration file in
				// the file system
				configIS = new FileInputStream(new File(configFileName));

				if (configIS == null) {
					// look for the user-specified FracLite configuration file
					// in the class path
					configIS = getClass().getResourceAsStream(configFileName);
				}
			}

			if (configIS == null) {
				// look for the default FracLite configuration file in the
				// class path
				configIS = getClass().getClassLoader().getResourceAsStream(
						FracLite.DEFAULT_CONFIGURATION_FILE_NAME);
			}

			Document doc = db.parse(configIS);

			NodeList nodes = null;

			nodes = doc.getElementsByTagName("itfDesc");
			for (int i = 0; i < nodes.getLength(); i++) {
				Element itfDescNode = (Element) nodes.item(i);
				String id = itfDescNode.getAttribute("id");
				String name = itfDescNode.getAttribute("name");
				String signature = itfDescNode.getAttribute("signature");
				InterfaceType it = tf.createFcItfType(name, signature, false,
						false, false);
				controlItfs.put(id, it);
			}

			nodes = doc.getElementsByTagName("membraneType");
			for (int i = 0; i < nodes.getLength(); i++) {
				Element membraneTypeNode = (Element) nodes.item(i);
				String membraneTypeId = membraneTypeNode.getAttribute("name");
				NodeList controllerNodes = membraneTypeNode
						.getElementsByTagName("controllerDesc");
				InterfaceType[] its = new InterfaceType[controllerNodes
						.getLength()];
				for (int j = 0; j < controllerNodes.getLength(); j++) {
					Element controllerDescNode = (Element) controllerNodes
							.item(j);
					String itfId = controllerDescNode.getAttribute("itf");
					String itfImpl = controllerDescNode.getAttribute("impl");
					its[j] = controlItfs.get(itfId);
					membraneImpls.put(membraneTypeId + its[j].getFcItfName(),
							itfImpl);
				}
				membraneTypes.put(membraneTypeId, tf.createFcType(its));
			}
		} catch (Exception e) {
			throw new ChainedInstantiationException(e, "Can't load config file");
		}
	}

	public ComponentType getControlType(String membraneTypeName)
			throws InstantiationException {
		ComponentType ct = membraneTypes.get(membraneTypeName);
		if (ct == null)
			throw new InstantiationException("Can't find membrane type :"
					+ membraneTypeName);
		return ct;
	}

	public String getControlImpl(String controllerDesc, InterfaceType it)
			throws InstantiationException {
		String impl = membraneImpls.get(controllerDesc + it.getFcItfName());
		if (impl == null)
			throw new InstantiationException("Can't find membrane impl :"
					+ controllerDesc + "/" + it);
		return impl;
	}

	public InterfaceType getComponentType() throws InstantiationException {
		return tf.createFcItfType("component",
				"org.objectweb.fractal.api.Component", false, false, false);
	}

	public String getComponentInterfaceImpl() {
		return "fr.jade.fraclite.BasicComponent";
	}
}
